import React, { ReactNode, RefObject, useMemo, useRef, useState } from 'react';
import {
  useBoolean,
  useLockFn,
  useMemoizedFn,
  useScroll,
  useVirtualList,
  useRequest,
  useThrottleEffect,
} from 'ahooks';
import { PaginationProps, PaginationResponse } from '@/common';
import { message } from 'antd';
import { t } from '@/locales/i18n';
import { CircleLoading } from '@/components/loading';
import { LoadingOutlined } from '@ant-design/icons';

type ItemHeight<T> = (index: number, data: T) => number;
const thresholdVal = 200;

export type ScrollListType = ({
  children,
  EmptyChildren,
  isLoading,
}: {
  children: React.ReactNode;
  EmptyChildren?: React.ReactNode;
  isLoading?: boolean;
}) => React.JSX.Element;

export function useVirtualScrollPagination<
  TParams extends PaginationProps,
  TData extends PaginationResponse,
>(
  api: (data: TParams) => Promise<TData>,
  {
    refreshDeps,
    itemHeight = 50,
    overscan = 10,

    pageSize = 10,
    defaultParams = {},
  }: {
    refreshDeps?: any[];

    itemHeight: number | ItemHeight<TData['list'][0]>;
    overscan?: number;

    pageSize?: number;
    defaultParams?: Record<string, any>;
  },
) {
  const containerRef = useRef<HTMLDivElement>(null);
  const wrapperRef = useRef(null);

  const [data, setData] = useState<TData['list']>([]);
  const [total, setTotal] = useState(0);
  const [isLoading, { setTrue, setFalse }] = useBoolean(false);

  const noMore = data.length >= total;

  const [list] = useVirtualList<TData['list'][0]>(data, {
    containerTarget: containerRef,
    wrapperTarget: wrapperRef,
    itemHeight,
    overscan,
  });

  const loadData = useLockFn(async (init = false) => {
    if (noMore && !init) return;

    const offset = init ? 0 : data.length;

    setTrue();

    try {
      const res = await api({
        offset,
        pageSize,
        ...defaultParams,
      } as TParams);

      setTotal(res.total);

      if (offset === 0) {
        // init or reload
        setData(res.list);
      } else {
        setData((prev) => [...prev, ...res.list]);
      }
    } catch (error: any) {
      message.error(error);
      console.error(error);
    }

    setFalse();
  });

  const scroll2Top = () => {
    if (containerRef.current) {
      containerRef.current.scrollTop = 0;
    }
  };

  const ScrollList = useMemoizedFn(
    ({
      children,
      EmptyChildren,
      isLoading,
      ...props
    }: {
      children: React.ReactNode;
      EmptyChildren?: React.ReactNode;
      isLoading?: boolean;
    }) => {
      return (
        <div ref={containerRef} className="overflow-y-auto" {...props}>
          <div ref={wrapperRef}>
            {children}
            {noMore && list.length > 0 && (
              <div className="py-4 text-center text-xs text-gray-600">
                {t('common.no_more_data')}
              </div>
            )}
          </div>

          {list.length === 0 && !isLoading && EmptyChildren && <>{EmptyChildren}</>}
        </div>
      );
    },
  );

  // 重新加载数据
  useRequest(
    async () => {
      loadData(true);
    },
    {
      manual: false,
      refreshDeps,
    },
  );

  // 检查是否滚动到最后
  const scroll = useScroll(containerRef);
  useThrottleEffect(
    () => {
      if (!containerRef.current || list.length === 0) return;
      const { scrollTop, scrollHeight, clientHeight } = containerRef.current;

      if (scrollTop + clientHeight >= scrollHeight - thresholdVal) {
        loadData(false);
      }
    },
    [scroll],
    {
      wait: 50,
    },
  );

  return {
    containerRef,
    scrollDataList: list,
    total,
    totalData: data,
    setData,
    isLoading,
    ScrollList,
    fetchData: loadData,
    scroll2Top,
  };
}

export function useScrollPagination<
  TParams extends PaginationProps,
  TData extends PaginationResponse,
>(
  api: (data: TParams) => Promise<TData>,
  {
    refreshDeps,
    scrollLoadType = 'bottom',

    pageSize = 10,
    params = {},
    EmptyTip,

    wait = 300,
    showLoadText = true,
  }: {
    refreshDeps?: any[];
    scrollLoadType?: 'top' | 'bottom';

    pageSize?: number;
    // app_id?: string;
    params?: Record<string, any>;
    EmptyTip?: React.JSX.Element;
    wait?: number;
    showLoadText?: boolean;
  },
) {
  const [data, setData] = useState<TData['list']>([]);
  const [total, setTotal] = useState(0);
  const [isLoading, { setTrue, setFalse }] = useBoolean(false);
  const isEmpty = total === 0 && !isLoading;

  const noMore = data.length >= total;

  const loadData = useLockFn(
    async (init = false, ScrollContainerRef?: RefObject<HTMLDivElement>) => {
      if (noMore && !init) return;

      const offset = init ? 0 : data.length;

      setTrue();

      try {
        const res = await api({
          offset,
          pageSize,
          // app_id,
          ...params,
        } as TParams);

        setTotal(res.total);

        if (scrollLoadType === 'top') {
          const prevHeight = ScrollContainerRef?.current?.scrollHeight || 0;
          const prevScrollTop = ScrollContainerRef?.current?.scrollTop || 0;
          // 使用 requestAnimationFrame 来调整滚动位置
          function adjustScrollPosition() {
            requestAnimationFrame(
              ScrollContainerRef?.current
                ? () => {
                    if (ScrollContainerRef?.current) {
                      const newHeight = ScrollContainerRef.current.scrollHeight;
                      const heightDiff = newHeight - prevHeight;
                      ScrollContainerRef.current.scrollTop = prevScrollTop + heightDiff;
                    }
                  }
                : adjustScrollPosition,
            );
          }

          setData((prevData) => (offset === 0 ? res.list : [...res.list, ...prevData]));
          adjustScrollPosition();
        } else {
          setData((prevData) => (offset === 0 ? res.list : [...prevData, ...res.list]));
        }
      } catch (error: any) {
        message.error(error);
        console.error(error);
      }

      setFalse();
    },
  );

  let ScrollRef = useRef<HTMLDivElement>(null);
  const ScrollData = useMemoizedFn(
    ({
      children,
      ScrollContainerRef,
      className = 'h-[calc(100vh-250px)] overflow-y-auto overflow-x-hidden',
      ...props
    }: {
      children: ReactNode;
      className?: string;
      ScrollContainerRef?: RefObject<HTMLDivElement>;
    } & React.HTMLAttributes<HTMLDivElement>) => {
      const ref = ScrollContainerRef || ScrollRef;
      const loadText = useMemo(() => {
        if (isLoading) return <div className="h-full w-full">{t('common.is_requesting')}</div>;
        if (noMore) return t('common.request_end');
        return t('common.click_request_more');
      }, [isLoading, noMore]);

      const scroll = useScroll(ref);

      // 监听滚动位置
      useThrottleEffect(
        () => {
          if (!ref?.current || noMore) return;
          const { scrollTop, scrollHeight, clientHeight } = ref.current;

          if (
            (scrollLoadType === 'bottom' &&
              scrollTop + clientHeight >= scrollHeight - thresholdVal) ||
            (scrollLoadType === 'top' && scrollTop < thresholdVal)
          ) {
            loadData(false, ref);
          }
        },
        [scroll],
        { wait: 50 },
      );

      const isRequestMore = useMemo(() => {
        return loadText === t('common.click_request_more');
      }, [loadText]);

      return (
        <div {...props} ref={ref} className={className}>
          {scrollLoadType === 'top' && total > 0 && isLoading && (
            <div className="mt-2 text-center text-xs text-[#718096]">
              {t('common.is_requesting')}
            </div>
          )}
          {children}
          {scrollLoadType === 'bottom' && !isEmpty && (
            <div
              className={`mt-2 cursor-pointer text-center text-xs text-[#718096] ${
                loadText ? 'cursor-pointer' : 'cursor-default'
              }`}
              onClick={() => {
                if (!isRequestMore) return;
                loadData(false);
              }}
            >
              {showLoadText && loadText}
            </div>
          )}
          {isEmpty && EmptyTip}
        </div>
      );
    },
  );

  // Reload data
  useRequest(
    async () => {
      loadData(true);
    },
    {
      manual: false,
      refreshDeps,
      debounceWait: wait,
    },
  );

  const refreshList = useMemoizedFn(() => {
    loadData(true);
  });

  return {
    ScrollData,
    isLoading,
    total: Math.max(total, data.length),
    data,
    setData,
    setTotal,
    fetchData: loadData,
    refreshList,
  };
}
